package cn.springsoter.core.redisson.config;

import java.util.List;
import java.util.Set;

import javax.annotation.PostConstruct;

import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.MasterSlaveServersConfig;
import org.redisson.config.SentinelServersConfig;
import org.redisson.config.SingleServerConfig;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import cn.springsoter.core.redis.constant.MessageConstant;
import cn.springsoter.core.redis.props.RedisProperties;
import cn.springsoter.core.redis.utils.HelperUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 
 * RedissonConfig配置
 * 
 * @author kevin
 *
 */
@Slf4j
public class RedissonConfig {
	
	private volatile RedissonClient redissonClient;
	private RedisProperties redisProperties;
	private static final String REDIS_PROTOCOL = "redis://";

	public RedissonConfig(RedisProperties redisProperties) {
		this.redisProperties = redisProperties;
	}

	@PostConstruct
	public RedissonClient getSingletonClient() {
		if (this.redissonClient == null) {
			synchronized (this) {
				if (this.redissonClient == null) {
					this.redissonClient = initialize();
				}
			}
		}
		return this.redissonClient;
	}

	/**
	 * 初始化
	 */
	private RedissonClient initialize() {
		log.info("********** initialize redis : {} . ", redisProperties.toString());
		String mode = redisProperties.getArchitecture();
		if (StringUtils.isBlank(mode)) {
			throw new RuntimeException(MessageConstant.REDIS_ARCHITECTURE_BLANK_MSG);
		}
		switch (mode) {
		case "standalone": // 单机
			return initializeSingle();
		case "sentinel": // 主从哨兵
			return initializeSentinel();
		case "cluster": // 集群
			return initializeCluster();
		case "replication": // 主从
			return initializeReplication();
		default:
			throw new RuntimeException(MessageConstant.REDIS_ARCHITECTURE_INVALID_VALUE_MSG);
		}
	}


	/**
	 * 单机模式
	 */
	private RedissonClient initializeSingle() {
		log.info("======= Redisson Use Standalone Mode ========");
		Config config = new Config();
		Set<String> nodes = HelperUtil.getHosts(redisProperties.getHost(), HelperUtil.SPRING_REDIS_HOST_MSG);
		// 获取第一个元素
		String node = nodes.iterator().next();
		node = node.startsWith(REDIS_PROTOCOL) ? node : REDIS_PROTOCOL + node;
		SingleServerConfig serverConfig = config.useSingleServer()
				.setAddress(node)
				.setDatabase(redisProperties.getDatabase())
				.setTimeout(Long.valueOf(redisProperties.getTimeout().toMillis()).intValue());
		if (StringUtils.isNotBlank(redisProperties.getPassword())) {
			serverConfig.setPassword(redisProperties.getPassword());
		}
		return Redisson.create(config);
	}

	/**
	 * 主从哨兵模式
	 */
	private RedissonClient initializeSentinel() {
		log.info("======= Redisson Use Sentinel Mode ========");
		Config config = new Config();
		Set<String> nodes = HelperUtil.getHosts(redisProperties.getHost(), HelperUtil.SPRING_REDIS_HOST_MSG);
		List<String> newNodes = Lists.newArrayListWithCapacity(nodes.size());
		nodes.forEach((index) -> newNodes.add(index.startsWith(REDIS_PROTOCOL) ? index : REDIS_PROTOCOL + index));
		SentinelServersConfig serverConfig = config.useSentinelServers()
				.setDatabase(redisProperties.getDatabase())
				.addSentinelAddress(newNodes.toArray(new String[0]))
				.setMasterName(redisProperties.getSentinel().getMaster())
				//10秒扫描一次
				.setScanInterval(10000)
				.setTimeout(Long.valueOf(redisProperties.getTimeout().toMillis()).intValue());
		if (StringUtils.isNotBlank(redisProperties.getPassword())) {
			serverConfig.setPassword(redisProperties.getPassword());
			serverConfig.setSentinelPassword(redisProperties.getPassword());
		}
		return Redisson.create(config);
	}

	/**
	 * 主从模式
	 */
	private RedissonClient initializeReplication() {
		log.info("======= Redisson Use Replication Mode ========");
		Config config = new Config();
		Set<String> masterNode = HelperUtil.getHosts(redisProperties.getMasterHost(), HelperUtil.SPRING_REDIS_MASTER_HOST_MSG);
		Set<String> slaveNodes = HelperUtil.getHosts(redisProperties.getSlavesHost(), HelperUtil.SPRING_REDIS_SLAVE_HOST_MSG);
		// master获取第一个元素
		String master = masterNode.iterator().next();
		master = master.startsWith(REDIS_PROTOCOL) ? master : REDIS_PROTOCOL + master;
		
		Set<String> newSlaveNodes = Sets.newLinkedHashSet();
		slaveNodes.forEach(slave -> newSlaveNodes.add(slave.startsWith(REDIS_PROTOCOL) ? slave : REDIS_PROTOCOL + slave));
		
		MasterSlaveServersConfig serverConfig = config.useMasterSlaveServers()
				.setDatabase(redisProperties.getDatabase())
				.setMasterAddress(master)
				.addSlaveAddress(newSlaveNodes.toArray(new String[newSlaveNodes.size()]))
				.setTimeout(Long.valueOf(redisProperties.getTimeout().toMillis()).intValue());
		if (StringUtils.isNotBlank(redisProperties.getPassword())) {
			serverConfig.setPassword(redisProperties.getPassword());
		}
		return Redisson.create(config);
	}

	/**
	 * 集群模式
	 */
	private RedissonClient initializeCluster() {
		log.info("======= Redisson Use Cluster Mode ========");
		Config config = new Config();
		Set<String> nodes = HelperUtil.getHosts(redisProperties.getHost(), HelperUtil.SPRING_REDIS_HOST_MSG);
		List<String> newNodes = Lists.newArrayListWithCapacity(nodes.size());
		nodes.forEach((index) -> newNodes.add(index.startsWith(REDIS_PROTOCOL) ? index : REDIS_PROTOCOL + index));
		ClusterServersConfig serverConfig = config.useClusterServers()
				.addNodeAddress(newNodes.toArray(new String[0]))
				.setTimeout(Long.valueOf(redisProperties.getTimeout().toMillis()).intValue());
		if (StringUtils.isNotBlank(redisProperties.getPassword())) {
			serverConfig.setPassword(redisProperties.getPassword());
		}
		return Redisson.create(config);
	}
	
	/**
	 * 关闭
	 */
	public void close() {
        if (this.redissonClient != null) {
            try {
                this.redissonClient.shutdown();
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
            this.redissonClient = null;
        }
    }
}
